from flask import Flask, render_template, request
from flask_socketio import SocketIO, send, emit
import random
import json
import os

app = Flask(__name__)
socketio = SocketIO(app)

# 游戏状态管理
games = {}

# 五子棋棋谱库（简化示例）
OPENING_BOOK = {
    # 格式: "棋盘状态": [推荐落子位置],
    "0" * 225: [(7, 7), (6, 7), (7, 6), (8, 7), (7, 8)],  # 开局推荐位置
    # 更多棋谱...
}

# 难度级别配置
DIFFICULTY = {
    "easy": {
        "depth": 1,  # 搜索深度
        "randomness": 0.7,  # 随机决策概率
        "pattern_weights": {  # 模式权重
            "open_two": 10,
            "half_open_two": 5,
            "open_three": 50,
            "half_open_three": 20,
            "open_four": 500,
            "half_open_four": 100,
            "five": 10000
        }
    },
    "medium": {
        "depth": 2,
        "randomness": 0.3,
        "pattern_weights": {
            "open_two": 15,
            "half_open_two": 8,
            "open_three": 70,
            "half_open_three": 30,
            "open_four": 700,
            "half_open_four": 200,
            "five": 10000
        }
    },
    "hard": {
        "depth": 3,
        "randomness": 0.1,
        "pattern_weights": {
            "open_two": 20,
            "half_open_two": 10,
            "open_three": 100,
            "half_open_three": 50,
            "open_four": 1000,
            "half_open_four": 500,
            "five": 10000
        }
    }
}


@app.route('/')
def index():
    return render_template('index.html')


@socketio.on('connect')
def handle_connect():
    print('客户端已连接')


@socketio.on('disconnect')
def handle_disconnect():
    print('客户端已断开连接')
    # 清理游戏状态
    for game_id, game in list(games.items()):
        if 'player_sid' in game and game['player_sid'] == request.sid:
            del games[game_id]
        elif 'players' in game and request.sid in game['players']:
            game['players'].remove(request.sid)
            if len(game['players']) == 0:
                del games[game_id]
            else:
                # 通知另一个玩家对手已离开
                for player_sid in game['players']:
                    if player_sid != request.sid:
                        emit('error', {'message': '对手已离开游戏'}, to=player_sid)


@socketio.on('create_game')
def handle_create_game():
    # 生成唯一游戏ID
    game_id = len(games) + 1
    while game_id in games:
        game_id += 1

    # 创建新游戏
    games[game_id] = {
        'players': [request.sid],
        'board': [[0 for _ in range(15)] for _ in range(15)],
        'current_player': 1,
        'winner': 0,
        'game_over': False
    }

    # 发送游戏ID给创建者
    emit('game_created', {'game_id': game_id})


@socketio.on('join_game')
def handle_join_game(data):
    game_id = data.get('game_id')

    if game_id not in games:
        emit('error', {'message': '游戏ID不存在'})
        return

    game = games[game_id]

    if len(game['players']) >= 2:
        emit('error', {'message': '游戏已满'})
        return

    # 添加玩家到游戏
    game['players'].append(request.sid)

    # 通知两个玩家游戏开始
    for i, player_sid in enumerate(game['players']):
        emit('game_started', {
            'player_number': i + 1,
            'game_id': game_id,
            'board': game['board']
        }, to=player_sid)


@socketio.on('create_ai_game')
def handle_create_ai_game(data):
    difficulty = data.get('difficulty', 'medium')

    # 生成唯一游戏ID
    game_id = len(games) + 1
    while game_id in games:
        game_id += 1

    # 创建与AI的游戏
    games[game_id] = {
        'player_sid': request.sid,
        'board': [[0 for _ in range(15)] for _ in range(15)],
        'current_player': 1,  # 玩家总是先手
        'winner': 0,
        'game_over': False,
        'ai_difficulty': difficulty,
        'ai_player': 2  # AI总是后手
    }

    # 发送游戏ID和AI难度给玩家
    emit('game_created', {
        'game_id': game_id,
        'ai_game': True,
        'difficulty': difficulty
    })

    # 通知玩家游戏开始
    emit('game_started', {
        'player_number': 1,
        'game_id': game_id,
        'board': games[game_id]['board']
    })


@socketio.on('make_move')
def handle_make_move(data):
    game_id = data.get('game_id')
    player = data.get('player')
    row = data.get('row')
    col = data.get('col')

    if game_id not in games:
        emit('error', {'message': '游戏不存在'})
        return

    game = games[game_id]

    if game['game_over']:
        emit('error', {'message': '游戏已结束'})
        return

    if player != game['current_player']:
        emit('error', {'message': '不是你的回合'})
        return

    if row < 0 or row >= 15 or col < 0 or col >= 15:
        emit('error', {'message': '无效的位置'})
        return

    if game['board'][row][col] != 0:
        emit('error', {'message': '该位置已有棋子'})
        return

    # 更新棋盘
    game['board'][row][col] = player

    # 检查是否有玩家获胜
    if check_winner(game['board'], row, col, player):
        game['winner'] = player
        game['game_over'] = True

    # 检查是否平局
    elif check_draw(game['board']):
        game['winner'] = 0
        game['game_over'] = True

    # 切换当前玩家
    else:
        game['current_player'] = 2 if player == 1 else 1

    # 发送移动结果给玩家
    if 'players' in game:  # PvP游戏
        for player_sid in game['players']:
            emit('move_made', {
                'player': player,
                'row': row,
                'col': col,
                'current_player': game['current_player']
            }, to=player_sid)
    else:  # PvE游戏
        emit('move_made', {
            'player': player,
            'row': row,
            'col': col,
            'current_player': game['current_player']
        }, to=game['player_sid'])

    # 如果游戏结束，发送游戏结束信息
    if game['game_over']:
        if 'players' in game:
            for player_sid in game['players']:
                emit('game_over', {'winner': game['winner']}, to=player_sid)
        else:
            emit('game_over', {'winner': game['winner']}, to=game['player_sid'])

    # 如果是AI游戏且轮到AI行动
    elif 'player_sid' in game and game['current_player'] == game['ai_player']:
        # 延迟一段时间，模拟思考
        socketio.sleep(0.5)

        # AI落子
        ai_row, ai_col = get_ai_move(game)
        game['board'][ai_row][ai_col] = game['ai_player']

        # 检查AI是否获胜
        if check_winner(game['board'], ai_row, ai_col, game['ai_player']):
            game['winner'] = game['ai_player']
            game['game_over'] = True

        # 检查是否平局
        elif check_draw(game['board']):
            game['winner'] = 0
            game['game_over'] = True

        # 切换当前玩家
        else:
            game['current_player'] = 1  # 回到玩家

        # 发送AI移动结果
        emit('move_made', {
            'player': game['ai_player'],
            'row': ai_row,
            'col': ai_col,
            'current_player': game['current_player']
        }, to=game['player_sid'])

        # 如果游戏结束，发送游戏结束信息
        if game['game_over']:
            emit('game_over', {'winner': game['winner']}, to=game['player_sid'])


# AI决策函数
def get_ai_move(game):
    board = game['board']
    difficulty = game['ai_difficulty']
    config = DIFFICULTY[difficulty]

    # 检查是否可以使用棋谱
    opening_move = check_opening_book(board)
    if opening_move:
        return opening_move

    # 根据难度决定是否随机落子
    if random.random() < config['randomness']:
        return get_random_move(board)

    # 否则使用Minimax算法
    best_score = float('-inf')
    best_move = None

    # 获取所有可能的落子位置（简化版：只考虑已有棋子周围的位置）
    possible_moves = get_possible_moves(board)

    for row, col in possible_moves:
        # 模拟AI落子
        board[row][col] = game['ai_player']

        # 评估局面
        score = minimax(board, config['depth'], float('-inf'), float('inf'), False, game['ai_player'],
                        config['pattern_weights'])

        # 撤销落子
        board[row][col] = 0

        if score > best_score:
            best_score = score
            best_move = (row, col)

    if best_move:
        return best_move
    else:
        # 如果没有找到最佳位置，随机落子
        return get_random_move(board)


# 检查棋谱库
def check_opening_book(board):
    # 将棋盘状态转换为字符串表示
    board_str = ''.join([str(cell) for row in board for cell in row])

    # 检查是否有对应的开局推荐
    if board_str in OPENING_BOOK:
        moves = OPENING_BOOK[board_str]
        return random.choice(moves)

    # 简化版：检查是否接近某个开局
    for pattern, moves in OPENING_BOOK.items():
        # 计算匹配度
        match_count = 0
        total_count = 0

        for i in range(len(board_str)):
            if pattern[i] != '0':
                total_count += 1
                if pattern[i] == board_str[i]:
                    match_count += 1

        # 如果匹配度超过80%，使用这个开局
        if total_count > 0 and match_count / total_count >= 0.8:
            return random.choice(moves)

    return None


# 获取随机有效落子位置
def get_random_move(board):
    possible_moves = []

    for i in range(15):
        for j in range(15):
            if board[i][j] == 0:
                possible_moves.append((i, j))

    if possible_moves:
        return random.choice(possible_moves)
    else:
        return (7, 7)  # 默认中央位置


# 获取所有可能的落子位置（简化版：只考虑已有棋子周围的位置）
def get_possible_moves(board):
    possible_moves = set()
    directions = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]

    for i in range(15):
        for j in range(15):
            if board[i][j] != 0:
                # 检查周围的空位
                for dx, dy in directions:
                    x = i + dx
                    y = j + dy
                    if 0 <= x < 15 and 0 <= y < 15 and board[x][y] == 0:
                        possible_moves.add((x, y))

    # 如果没有找到，返回所有空位
    if not possible_moves:
        for i in range(15):
            for j in range(15):
                if board[i][j] == 0:
                    possible_moves.add((i, j))

    return list(possible_moves)


# Minimax算法（带Alpha-Beta剪枝）
def minimax(board, depth, alpha, beta, is_maximizing, ai_player, weights):
    # 检查游戏是否结束
    if check_winner(board, 0, 0, ai_player):  # AI获胜
        return weights['five']
    elif check_winner(board, 0, 0, 3 - ai_player):  # 玩家获胜
        return -weights['five']
    elif check_draw(board):  # 平局
        return 0

    if depth == 0:
        # 评估当前局面
        return evaluate_board(board, ai_player, weights)

    if is_maximizing:
        max_score = float('-inf')
        for i in range(15):
            for j in range(15):
                if board[i][j] == 0:
                    board[i][j] = ai_player
                    score = minimax(board, depth - 1, alpha, beta, False, ai_player, weights)
                    board[i][j] = 0
                    max_score = max(score, max_score)
                    alpha = max(alpha, score)
                    if beta <= alpha:
                        break  # Beta剪枝
            if beta <= alpha:
                break
        return max_score
    else:
        min_score = float('inf')
        for i in range(15):
            for j in range(15):
                if board[i][j] == 0:
                    board[i][j] = 3 - ai_player  # 对手
                    score = minimax(board, depth - 1, alpha, beta, True, ai_player, weights)
                    board[i][j] = 0
                    min_score = min(score, min_score)
                    beta = min(beta, score)
                    if beta <= alpha:
                        break  # Alpha剪枝
            if beta <= alpha:
                break
        return min_score


# 评估棋盘局面
def evaluate_board(board, player, weights):
    score = 0

    # 评估玩家的得分
    player_score = evaluate_player(board, player, weights)

    # 评估对手的得分（负分）
    opponent_score = evaluate_player(board, 3 - player, weights)

    return player_score - opponent_score


# 评估单个玩家的局面得分
def evaluate_player(board, player, weights):
    score = 0

    # 检查所有可能的五连子位置
    directions = [(0, 1), (1, 0), (1, 1), (1, -1)]  # 水平、垂直、对角线、反对角线

    for i in range(15):
        for j in range(15):
            for dx, dy in directions:
                # 检查是否可以形成五连子
                if i + 4 * dx < 0 or i + 4 * dx >= 15 or j + 4 * dy < 0 or j + 4 * dy >= 15:
                    continue

                # 统计连续的棋子数
                count = 0
                empty_ends = 0

                for k in range(5):
                    x = i + k * dx
                    y = j + k * dy

                    if board[x][y] == player:
                        count += 1
                    elif board[x][y] == 0:
                        empty_ends += 1
                    else:
                        # 被对手阻断
                        break
                else:
                    # 检查两端是否开放
                    open_ends = 0

                    # 检查起点的前一个位置
                    start_x = i - dx
                    start_y = j - dy
                    if start_x < 0 or start_x >= 15 or start_y < 0 or start_y >= 15 or board[start_x][start_y] == 0:
                        open_ends += 1

                    # 检查终点的后一个位置
                    end_x = i + 5 * dx
                    end_y = j + 5 * dy
                    if end_x < 0 or end_x >= 15 or end_y < 0 or end_y >= 15 or board[end_x][end_y] == 0:
                        open_ends += 1

                    # 根据模式评分
                    if count == 5:
                        score += weights['five']
                    elif count == 4:
                        if open_ends == 2:
                            score += weights['open_four']
                        elif open_ends == 1:
                            score += weights['half_open_four']
                    elif count == 3:
                        if open_ends == 2:
                            score += weights['open_three']
                        elif open_ends == 1:
                            score += weights['half_open_three']
                    elif count == 2:
                        if open_ends == 2:
                            score += weights['open_two']
                        elif open_ends == 1:
                            score += weights['half_open_two']

    return score


# 检查是否有玩家获胜
def check_winner(board, row, col, player):
    directions = [
        (0, 1),  # 水平
        (1, 0),  # 垂直
        (1, 1),  # 对角线
        (1, -1)  # 反对角线
    ]

    for dx, dy in directions:
        count = 1  # 当前位置已经有一个棋子

        # 向正方向检查
        for i in range(1, 5):
            r = row + i * dx
            c = col + i * dy
            if r < 0 or r >= 15 or c < 0 or c >= 15 or board[r][c] != player:
                break
            count += 1

        # 向反方向检查
        for i in range(1, 5):
            r = row - i * dx
            c = col - i * dy
            if r < 0 or r >= 15 or c < 0 or c >= 15 or board[r][c] != player:
                break
            count += 1

        if count >= 5:
            return True

    return False


# 检查是否平局
def check_draw(board):
    for row in board:
        for cell in row:
            if cell == 0:
                return False  # 还有空位，不是平局
    return True  # 棋盘已满，平局


if __name__ == '__main__':
    socketio.run(app, debug=True)